/******************************************************************************
* Copyright (c) 2007 Alexandru Scvortov
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/


/*!
    @projectname leandoc

    @projectversion 0.3

    @author Alexandru Scvortov <scvalex@gmail.com>

    @site http://code.google.com/p/leandoc/
*/


#include "common.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

//! Null terminated list of pointers to the file names to parse
static char **files_to_check;

//! Contains the path to the CSS styles file
static char *styles_file;

static int only_with_comments;

static char *original_style = {
#include "original_style.inc"
};

//! Prints version and copyright information
static void version() {
    fprintf(stderr, "leandoc v0.1\n"
           "Run 'leandoc -h' for help.\n"
           "\n"
           "Copyright (C) 2007 Alexandru Scvortov <scvalex@gmail.com>\n"
           "This is free software; see the source for copying conditions.\n"
           "There is NO warranty; not even for MERCHANTABILITY or FITNESS\n"
           "FOR A PARTICULAR PURPOSE.\n\n");
}

//! Basicly outputs a man page
static void usage() {
    fprintf(stderr, "NAME\n"
           "  leandoc - generate documentation from files\n"
           "\n"
           "SYNOPSIS\n"
           "  leandoc [OPTION] file1 file2 ...\n"
           "\n"
           "DESCRIPTION\n"
           "  Generate documentation from specified files.\n"
           "\n"
           "  -h\n"
           "    print this help message\n"
           "  -v\n"
           "    print version information\n"
           "  -c\n"
           "    specifies the CSS file to be used\n"
           "  -n\n"
           "    only print functions that are commented\n");
    fprintf(stderr, "\n");
}

//! Handles unknown options (i.e. complains, prints help and exits)
static void unknown_opt() {
    fprintf(stderr, "Missing or unknown option.\n\n");
    usage();
}

//! Performs cleanup actions and exits the programme
void move_out(int ret_code) {
    check_and_free(files_to_check);
    check_and_free(current_line);
    check_and_free(current_value);
    check_and_free(project_name);
    check_and_free(project_version);
    check_and_free(author);
    check_and_free(site);

    FunctionNode *fn;
    fn = functions;
    while (fn) {
        FunctionNode *next = fn->next;
        function_node_remove(fn);
        fn = next;
    }

    CommentNode *cn;
    cn = comments;
    while (cn) {
        CommentNode *next = cn->next;
        comment_node_remove(cn);
        cn = next;
    }

    VarNode *vn;
    vn = variables;
    while (vn) {
        VarNode *next = vn->next;
        var_node_remove(vn);
        vn = next;
    }

    exit(ret_code);
}

//! Prints the internal stylesheet
static void print_styles() {
    printf("    <style type=\"text/css\">\n"
           "");

    FILE *f = fopen(styles_file, "r");
    if (f) {
        if (!f) {
            printf("Can't open CSS styles file: %s\n", styles_file);
            move_out(1);
        }

        char buf[1024];
        while (fgets(buf, 1024, f))
            printf("%s", buf);

        fclose(f);
    } else {
        printf("%s\n", original_style);
    }

    printf(""
           "    </style>\n");
}

//! Converts text to valid XML input (i.e. no ilegal characters)
static char* normalize_for_xml(char *text) {
    if (!text) {
        char *tmp = (char*)malloc(sizeof(char));
        tmp[0] = 0;
        return tmp;
    }

    char *nt = (char*)malloc(strlen(text) * 5 * sizeof(char));
    char *s = text;
    char *d = nt;
    while (*s) {
        if ('&' == *s) {
            *d++ = '&';
            *d++ = 'a';
            *d++ = 'm';
            *d++ = 'p';
            *d++ = ';';
        } else if ('<' == *s) {
            *d++ = '&';
            *d++ = 'l';
            *d++ = 't';
            *d++ = ';';
        } else if ('>' == *s) {
            *d++ = '&';
            *d++ = 'g';
            *d++ = 't';
            *d++ = ';';
        } else if ('\"' == *s) {
            *d++ = '&';
            *d++ = 'q';
            *d++ = 'u';
            *d++ = 'o';
            *d++ = 't';
            *d++ = 'o';
        } else if ('\'' == *s) {
            *d++ = '&';
            *d++ = '#';
            *d++ = '3';
            *d++ = '9';
            *d++ = ';';
        } else
            *d++ = *s;
        ++s;
    }
    *d = 0;

    return nt;
}

//! Prints the first part of the XHTML file
static void print_xhtml_head() {
    char *pn = normalize_for_xml(project_name);
    char *pv = normalize_for_xml(project_version);
    char *au = normalize_for_xml(author);
    char *st = normalize_for_xml(site);

    printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
           "\n"
           "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
           "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
           "\n"
           "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n"
           "  <head>\n"
           "    <meta http-equiv=\"Content-Type\" content=\"application/xhtml+xml; charset=utf-8\" />\n"
           "\n"
           "    <meta name=\"keywords\" content=\"\" />\n"
           "\n"
           "    <meta name=\"description\" content=\"\" />\n"
           "\n"
           "    <meta name=\"generator\" content=\"leandoc\" />\n"
           "\n"
           "    <title>%s - %s - Project Documentation</title>\n"
           "\n", pn, pv);
    print_styles();
    printf("  </head>\n"
           "\n"
           "  <body>\n"
           "    <div id=\"konqbghack\">\n"
           "    <div id=\"titleContainer\">"
           "      <h1 id=\"title\"><a href=\"%s\">%s - %s</a></h1>\n"
           "      <div id=\"subtitle\">%s</div>\n"
           "    </div>"
           "    <div id=\"content\">\n", st, pn, pv, au);

    check_and_free(st);
    check_and_free(au);
    check_and_free(pv);
    check_and_free(pn);
}

//! Prints the final part of the XHTML file
static void print_xhtml_tail() {
    printf("      <div id=\"footer\">\n"
           "        <div class=\"navbar\">\n"
           "          <ul>\n"
           "            <li><a href=\"http://validator.w3.org/check?uri=referer\">Valid XHTML 1.1</a></li>\n"
           "            <li><a href=\"http://jigsaw.w3.org/css-validator/\">Valid CSS!</a></li>\n"
           "            <li><a href=\"http://code.google.com/p/leandoc/\">Generated by leandoc</a></li>\n"
           "            <li><a href=\"http://www.informationarchitects.jp/100E2R/\">Easy-2-Read</a></li>\n"
           "          </ul>\n"
           "        </div>\n"
           "      </div>\n"
           "    </div>\n"
           "    </div>\n"
           "  </body>\n"
           "</html>\n");
}

//! Prints the function index
static void print_function_index() {
    FunctionNode *fn;
    fn = functions;
    printf("        <h2 class=\"sectionHead\">Functions</h2>\n");
    printf("        <ul id=\"functionIndex\">\n");
    while (fn) {
        if (!only_with_comments || (only_with_comments && fn->c_node)) {
            printf("          <li class=\"function\">");
            int second_run_necessary = 0;
            // First run
            WordNode *it = fn->child;
            while (it) {
                if (WordNodeName == it->type) {
                    if (fn->name->value) {
                        char tmp[256];
                        snprintf(tmp, 256, "<a href=\"#%s\">", fn->name->value);
                        printf(tmp);
                    }
                    printf("<span class=\"functionName\">%s</span>", it->value);
                    if (fn->name->value)
                        printf("</a>");
                } else if (WordNodeUnknown == it->type)
                    printf("<span>%s</span>", it->value);
                else
                    second_run_necessary = 1;
                NEXT(it);
            }

            if (second_run_necessary) {
                // Second run
                printf("<span>[</span>");
                it = fn->child;
                while (it) {
                    if (WordNodeFunctionType == it->type)
                        printf("<span><em>%s</em></span>", it->value);
                    NEXT(it);
                }
                printf("<span>]</span>");
            }
            printf("          </li>\n");
        }
        NEXT(fn);
    }
    printf("        </ul>");
}

//! Prints an index of functions
static void print_variable_index() {
    VarNode *vn;
    vn = variables;
    printf("        <h2 class=\"sectionHead\">Variables</h2>\n");
    printf("        <ul id=\"variableIndex\">\n");

    while (vn) {
        if (!only_with_comments || (only_with_comments && vn->c_node)) {
            printf("          <li class=\"variable\">");
            int second_run_necessary = 0;
            // First run
            WordNode *it = vn->child;
            while (it) {
                if (WordNodeName == it->type) {
                    if (vn->name->value) {
                        char tmp[256];
                        snprintf(tmp, 256, "<a href=\"#%s\">", vn->name->value);
                        printf(tmp);
                    }
                    printf("<span class=\"variableName\">%s</span>", it->value);
                    if (vn->name->value)
                        printf("</a>");
                } else if (WordNodeUnknown == it->type)
                    printf("<span>%s</span>", it->value);
                else
                    second_run_necessary = 1;
                NEXT(it);
            }

            if (second_run_necessary) {
                // Second run
                printf("<span>[</span>");
                it = vn->child;
                while (it) {
                    if (WordNodeVariableType == it->type)
                        printf("<span><em>%s</em></span>", it->value);
                    NEXT(it);
                }
                printf("<span>]</span>");
            }
            printf("          </li>\n");
        }
        NEXT(vn);
    }

    printf("        </ul>");
}

//! Prints the detailed function descriptions
static void print_function_details() {
    printf("        <h2 class=\"sectionHead\">Function descriptions</h2>\n");
    FunctionNode *fn = functions;
    while (fn) {
        if (!only_with_comments || (only_with_comments && fn->c_node)) {
            printf("          <div class=\"functionBlock\">");
            if (fn->name->value) {
                char tmp[256];
                snprintf(tmp, 256, "<a href=\"#\" id=\"%s\" />", fn->name->value);
                printf(tmp);
            }
            printf("            <div class=\"function\">");
            int second_run_necessary = 0;
            WordNode *it = fn->child;
            while (it) {
                if ((WordNodeUnknown == it->type) || (WordNodeName == it->type))
                    printf("<span>%s</span>", it->value);
                else
                    second_run_necessary = 1;
                NEXT(it);
            }
            if (second_run_necessary) {
                printf("<span>[</span>");
                it = fn->child;
                while (it) {
                    if (WordNodeFunctionType == it->type)
                        printf("<span><em>%s</em></span>", it->value);
                    NEXT(it);
                }
                printf("<span>]</span>");
            }
            printf("            </div>\n");

			printf("            <div class=\"whereFound\">");
			printf("              <span>found:</span><span class=\"fileName\">%s</span><span>line:</span><span class=\"lineNum\">%d</span>", fn->file_name, fn->line_num);
			printf("            </div>");

            if (fn->c_node) {
                printf("            <div class=\"description\">\n");
                DocNode *dn = fn->c_node->child;
                while (dn) {
                    if ((DocNodeText == dn->type) || (DocNodeUnknown == dn->type)) {
                        char indent_style[16];
                        sprintf(indent_style, "indent%d", dn->indent_level);

                        char *tmp = normalize_for_xml(dn->value);
                        printf("              <p class=\"%s\">%s</p>\n", indent_style, tmp);
                        free(tmp);
                    } else if (DocNodeReturn == dn->type) {
                        char *tmp = normalize_for_xml(dn->value);
                        printf("              <div class=\"separateBlock\">");
                        printf("              <h3>Returns:</h3>");
                        printf("                <p>%s</p>\n", tmp);
                        printf("              </div>");
                        free(tmp);
                    }
                    NEXT(dn);
                }
                printf("            </div>");
            }
            printf("          </div>");
        }
        NEXT(fn);
    }
}

//! Prints the detailed variable descriptions
static void print_variable_details() {
    printf("        <h2 class=\"sectionHead\">Variable descriptions</h2>\n");
    VarNode *vn = variables;
    while (vn) {
        if (!only_with_comments || (only_with_comments && vn->c_node)) {
            printf("          <div class=\"variableBlock\">");
            if (vn->name->value) {
                char tmp[256];
                snprintf(tmp, 256, "<a href=\"#\" id=\"%s\" />", vn->name->value);
                printf(tmp);
            }
            printf("            <div class=\"variable\">");
            int second_run_necessary = 0;
            WordNode *it = vn->child;
            while (it) {
                if ((WordNodeUnknown == it->type) || (WordNodeName == it->type))
                    printf("<span>%s</span>", it->value);
                else
                    second_run_necessary = 1;
                NEXT(it);
            }
            if (second_run_necessary) {
                printf("<span>[</span>");
                it = vn->child;
                while (it) {
                    if (WordNodeVariableType == it->type)
                        printf("<span><em>%s</em></span>", it->value);
                    NEXT(it);
                }
                printf("<span>]</span>");
            }
            printf("            </div>\n");

			printf("            <div class=\"whereFound\">");
			printf("              <span>found:</span><span class=\"fileName\">%s</span><span>line:</span><span class=\"lineNum\">%d</span>", vn->file_name, vn->line_num);
			printf("            </div>");

            if (vn->c_node) {
                printf("            <div class=\"description\">\n");
                DocNode *dn = vn->c_node->child;
                while (dn) {
                    if ((DocNodeText == dn->type) || (DocNodeUnknown == dn->type)) {
                        char indent_style[16];
                        sprintf(indent_style, "indent%d", dn->indent_level);

                        char *tmp = normalize_for_xml(dn->value);
                        printf("              <p class=\"%s\">%s</p>\n", indent_style, tmp);
                        free(tmp);
                    } else if (DocNodeReturn == dn->type) {
                        char *tmp = normalize_for_xml(dn->value);
                        printf("              <div class=\"separateBlock\">");
                        printf("              <h3>Returns:</h3>");
                        printf("                <p>%s</p>\n", tmp);
                        printf("              </div>");
                        free(tmp);
                    }
                    NEXT(dn);
                }
                printf("            </div>");
            }
            printf("          </div>");
        }
        NEXT(vn);
    }
}

//! Prints a separator (hline)
static void print_separator() {
    printf("        <hr />");
}

/*!
    Main parse function

    Tasks:

      * generate base XHTML elements
*/
static void do_parse() {
    int i = 0;
    for (; files_to_check[i]; ++i)
        scan(files_to_check[i]);

    print_xhtml_head();

    print_function_index();

    print_variable_index();

    print_separator();

    print_function_details();

    print_variable_details();

    print_xhtml_tail();
}

int main(int argc, char *argv[]) {
    // Initialisations
    max_line_length = 1024;
    open_file = 0;
    files_to_check = 0;
    current_line = 0;
    current_value = 0;
	current_file = 0;
    comments = 0;
    functions = 0;
    variables = 0;
    styles_file = 0;
    project_name = 0;
    project_version = 0;
    author = 0;
    site = 0;

    // Programme starts here
    opterr = 0;
    only_with_comments = 0;

    int c;
    while ((c = getopt(argc, argv, "hvs:n")) != -1) {
        switch (c) {
            case 'h':
                version();
                usage();
                exit(0);
            case 'v':
                version();
                exit(0);
            case 's':
                styles_file = optarg;
                break;
            case 'n':
                only_with_comments = 1;
                break;
            case '?':
                if ('s' == optopt) 
                    fprintf(stderr, "Option -s requires an argument.\n");
            default:
                unknown_opt();
                exit(0);
        }
    }

    int i = optind;
    files_to_check = (char**)malloc((argc - optind + 1) * sizeof(char*));
    FILE *tf;
    for (; i < argc; ++i) {
        tf = fopen(argv[i], "r");
        if (!tf) {
            fprintf(stderr, "Can't open %s for reading. Check permissions and try again.\n", argv[i]);
            move_out(1);
        }
        files_to_check[i - optind] = argv[i];
        fclose(tf);
    }
    files_to_check[i - optind] = 0;

    do_parse();

    move_out(0);
}
